#include "console.h"
#include "string_utils.h"
#include <iostream>

namespace dave = cpac;
namespace dave
{
    console g_console;

    namespace console_functions
    {
        void default_function(vector< string >::type const& in_args)
        {
            stringstream message;
            message << "Command <" << in_args[0] << "> does not exist";
            g_console.print(message.str());
        }

        /*void inspect_variable(vector< string >::type const& in_args)
        {
            if(in_args.size() != 2)
            {
                g_console.print("Incorrect number of arguments specified!");
                g_console.print("    Usage is: /inspect var-name");
                return;
            }
            stringstream message;
            auto found = g_console._variables.find(in_args[1]);
            if(found != std::end(g_console._variables))
            {
                message << "$" << in_args[1] << " ";
                switch(found->second._type)
                {
                case TYPE_BOOL:       message << std::boolalpha << *(reinterpret_cast< bool const* const >(found->second._data)); break;
                case TYPE_UCHAR:      message << *(reinterpret_cast< unsigned char const* const >(found->second._data));          break;
                case TYPE_CHAR:       message << *(reinterpret_cast< char const* const >(found->second._data));                   break;
                case TYPE_USHORT:     message << *(reinterpret_cast< unsigned short const* const >(found->second._data));         break;
                case TYPE_SHORT:      message << *(reinterpret_cast< short const* const >(found->second._data));                  break;
                case TYPE_UINT:       message << *(reinterpret_cast< unsigned int const* const >(found->second._data));           break;
                case TYPE_INT:        message << *(reinterpret_cast< int const* const >(found->second._data));                    break;
                case TYPE_ULONG:      message << *(reinterpret_cast< unsigned long const* const >(found->second._data));          break;
                case TYPE_LONG:       message << *(reinterpret_cast< long const* const >(found->second._data));                   break;
                case TYPE_ULONGLONG:  message << *(reinterpret_cast< unsigned long long const* const >(found->second._data));     break;
                case TYPE_LONGLONG:   message << *(reinterpret_cast< long long const* const >(found->second._data));              break;
                case TYPE_FLOAT:      message << *(reinterpret_cast< float const* const >(found->second._data));                  break;
                case TYPE_DOUBLE:     message << *(reinterpret_cast< double const* const >(found->second._data));                 break;
                case TYPE_LONGDOUBLE: message << *(reinterpret_cast< long double const* const >(found->second._data));            break;
                default:              message.str("ERROR: unknown type!"); break;
                }; 
                g_console.print(message.str());
            }
            else
            {
                message << "variable " << in_args[1] << " not found";
                g_console.print(message.str());
            }
        }*/

        /*void set_variable(cpac::vector< cpac::string >::type const& in_args)
        {
            if(in_args.size() != 3)
            {
                g_console.print("Incorrect number of arguments specified!");
                g_console.print("    Usage is: /set var-name value");
                return;
            }
            auto found = g_console._variables.find(in_args[1]);
            if(found != std::end(g_console._variables))
            {
                switch(found->second._type)
                {
                case TYPE_BOOL:       *(reinterpret_cast< bool * const >(found->second._data)) = from_string< bool >(in_args[2]); break;
                case TYPE_UCHAR:      *(reinterpret_cast< unsigned char * const >(found->second._data)) = from_string< unsigned char >(in_args[2]);          break;
                case TYPE_CHAR:       *(reinterpret_cast< char * const >(found->second._data)) = from_string< char >(in_args[2]);                   break;
                case TYPE_USHORT:     *(reinterpret_cast< unsigned short * const >(found->second._data)) = from_string< unsigned short >(in_args[2]);         break;
                case TYPE_SHORT:      *(reinterpret_cast< short * const >(found->second._data)) = from_string< short >(in_args[2]);                  break;
                case TYPE_UINT:       *(reinterpret_cast< unsigned int * const >(found->second._data)) = from_string< unsigned int >(in_args[2]);           break;
                case TYPE_INT:        *(reinterpret_cast< int * const >(found->second._data)) = from_string< int >(in_args[2]);                    break;
                case TYPE_ULONG:      *(reinterpret_cast< unsigned long * const >(found->second._data)) = from_string< unsigned long >(in_args[2]);          break;
                case TYPE_LONG:       *(reinterpret_cast< long * const >(found->second._data)) = from_string< long >(in_args[2]);                   break;
                case TYPE_ULONGLONG:  *(reinterpret_cast< unsigned long long * const >(found->second._data)) = from_string< unsigned long long >(in_args[2]);     break;
                case TYPE_LONGLONG:   *(reinterpret_cast< long long * const >(found->second._data)) = from_string< long long >(in_args[2]);              break;
                case TYPE_FLOAT:      *(reinterpret_cast< float * const >(found->second._data)) = from_string< float >(in_args[2]);                  break;
                case TYPE_DOUBLE:     *(reinterpret_cast< double * const >(found->second._data)) = from_string< double >(in_args[2]);                 break;
                case TYPE_LONGDOUBLE: *(reinterpret_cast< long double * const >(found->second._data)) = from_string< long double >(in_args[2]);            break;
                default:              g_console.print("ERROR: unknown type!"); break;
                };
            }
            else
            {
                stringstream message("variable " + in_args[1] + " not found");
                g_console.print(message.str());
            }
        }*/

        /*void quit(cpac::vector< cpac::string >::type const& in_args)
        {
            auto found = g_console._variables.find("running");
            if(found != cend(g_console._variables))
            {
                *(reinterpret_cast< bool* >(found->second._data)) = false;
            }
        }*/
    }

    console::console()
    {
        set_default_command(console_functions::default_function);

       /* add_function("/inspect", console_functions::inspect_variable);
        add_function("/set", console_functions::set_variable);
        add_function("/quit", console_functions::quit);*/
    }

    console::console(console&& in_rhs)
        : _command_buffer(0),
          _text_buffer(0),
          _command_line(0)
    {
        *this = std::move(in_rhs);
    }
        
    console& console::operator=(console&& in_rhs)
    {
        std::swap(_command_buffer,  in_rhs._command_buffer);
        std::swap(_default_command, in_rhs._default_command);
        std::swap(_functions,       in_rhs._functions);
        std::swap(_variables,       in_rhs._variables);
        std::swap(_text_buffer,     in_rhs._text_buffer);
        std::swap(_command_line,    in_rhs._command_line);
        return *this;
    }

    console::~console()
    {
    }

    void console::remove_item(string const& in_name)
    {
    }

    void console::set_default_command(console_function in_function)
    {
        _default_command = in_function;
    }

    void console::print(string const& in_text)
    {
        _text_buffer.push_back(in_text);
    }

    void console::render()
    {
        if(!_text_buffer.empty())
        {
            std::for_each(cbegin(_text_buffer), cend(_text_buffer), [](string const& in_line)
            {
                std::cout << in_line << "\n";
            });
            _text_buffer.clear();
        }
    }

    void console::parse_command_line(string const& in_command)
    {
        if(in_command.empty()) return;

        vector< string >::type arguments;

        _command_buffer.push_back(in_command);

        string_tokeniser< is_space >::tokenise(in_command, arguments);

        if(arguments.empty()) return;

        if(arguments[0].front() == '/')
        {
            auto function = _functions.find(string(std::begin(arguments[0]), std::end(arguments[0])));
            if(function != std::end(_functions))
                function->second(arguments);
            else
            {
                stringstream message;
                message << "Error: could not find function " << arguments[0];
                print(message.str());
            }
        }
        else
        {
            print("Error: Use a leading forward slash to execute a command.");
        }

    }
}